using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using System.Text;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using TicTacToe.GS;

namespace TicTacToe
{
    public enum Screen { Main, Option, Multi }

    public class SinglePlay : GamePlay
    {

        private Texture2D gridTexture;
        private Rectangle gridRectangle;

        private Texture2D resetButton;
        private Rectangle resetButtonPosition;

        private bool showingSaveButton;
        private Texture2D saveButton;
        private Rectangle saveAndLoadButtonPosition;
        private Texture2D loadButton;

        private Texture2D optionButton;
        private Rectangle optionButtonPosition;

        private Texture2D undoButton;
        private Rectangle undoButtonPosition;

        private Texture2D oPiece;
        private Texture2D xPiece;

        private Texture2D oWinner;
        private Texture2D xWinner;
        private Texture2D noWinner;

        private Texture2D oTurn;
        private Texture2D xTurn;
        
        private int size;// duplicate of boardSize
        private GameLogic logic;

        private SpriteFont statisticsFont;
        private Vector2 FontPos;

        // the game state of TicTacToe
        private IGameState gameState;

        private int backBufferedWidth;
        private int backBufferedHeight;
        private int squareEdge;

        private int numDraw;
        private int numXWin;
        private int numOWin;

        public SinglePlay(Game gameMain, int backBufferedWidth, int backBufferedHeight) : base(gameMain)
        {
            this.backBufferedWidth = backBufferedWidth;
            this.backBufferedHeight = backBufferedHeight;

            gameScreen = Screen.Main;

            numXWin = 0;
            numOWin = 0;
            numDraw = 0;

            StyleTexture = (Style_Texture)Enum.Parse(typeof(Style_Texture), OptionSaver.Get(OptionSaver.X_O_STYLE).ToString(), false);

            showingSaveButton = true;
            showingSaveButton = !GameSaver.CanLoad();
        }

        public override void LoadContent()
        {
            spriteBatch = new SpriteBatch(gameMain.GraphicsDevice);
            gridRectangle = new Rectangle(0, 0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height);
            resetButton = gameMain.Content.Load<Texture2D>("TicTacToe_Reset");
            resetButtonPosition = new Rectangle(spriteBatch.GraphicsDevice.Viewport.Width / 2 - (resetButton.Width / 2), spriteBatch.GraphicsDevice.Viewport.Height - 95, resetButton.Width, resetButton.Height);
            saveButton = gameMain.Content.Load<Texture2D>("SaveButton");
            loadButton = gameMain.Content.Load<Texture2D>("LoadButton");
            saveAndLoadButtonPosition = new Rectangle(resetButtonPosition.X - 10, resetButtonPosition.Y - resetButtonPosition.Height, saveButton.Width, saveButton.Height);
            optionButton = gameMain.Content.Load<Texture2D>("OptionButton");
            optionButtonPosition = new Rectangle(saveAndLoadButtonPosition.X + saveButton.Width, saveAndLoadButtonPosition.Y, optionButton.Width, optionButton.Height);
            undoButton = gameMain.Content.Load<Texture2D>("UndoButton");
            undoButtonPosition = new Rectangle(optionButtonPosition.X + saveButton.Width, saveAndLoadButtonPosition.Y, undoButton.Width, undoButton.Height);

            oWinner = gameMain.Content.Load<Texture2D>("TicTacToe_O_Winner_");
            xWinner = gameMain.Content.Load<Texture2D>("TicTacToe_X_Winner_");
            noWinner = gameMain.Content.Load<Texture2D>("TicTacToe_Draw_");

            oTurn = gameMain.Content.Load<Texture2D>("TicTacToe_O_Turn");
            xTurn = gameMain.Content.Load<Texture2D>("TicTacToe_X_Turn");

            LoadBoardAnd_X_O();

            statisticsFont = gameMain.Content.Load<SpriteFont>("Segoe UI Mono");

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(gameMain.GraphicsDevice);
        }

        public GameLogic LogicOfGame
        {
            get { return logic; }
        }

        public void LoadBoard()
        {
            if (SizeOfBoard == BoardSize.Six_x_Six)
                gridTexture = gameMain.Content.Load<Texture2D>("TicTacToe_Grid_6x6");
            else if (SizeOfBoard == BoardSize.Three_x_Three)
                gridTexture = gameMain.Content.Load<Texture2D>("TicTacToe_Grid_3x3");
        }

        public void Load_X_O()
        {
            if (SizeOfBoard == BoardSize.Six_x_Six)
            {
                if (StyleTexture == Style_Texture.Classic)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_O_6x6");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_X_6x6");
                }
                else if (StyleTexture == Style_Texture.Modern)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_RebelAlliance_6x6");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_GalacticEmpire_6x6");
                }
            }
            else if (SizeOfBoard == BoardSize.Three_x_Three)
            {
                if (StyleTexture == Style_Texture.Classic)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_O_3x3");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_X_3x3");
                }
                else if (StyleTexture == Style_Texture.Modern)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_RebelAlliance");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_GalacticEmpire");
                }
            }
        }

        public void LoadBoardAnd_X_O()
        {
            if (SizeOfBoard == BoardSize.Six_x_Six)
            {
                gridTexture = gameMain.Content.Load<Texture2D>("TicTacToe_Grid_6x6");

                if (StyleTexture == Style_Texture.Classic)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_O_6x6");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_X_6x6");
                }
                else if (StyleTexture == Style_Texture.Modern)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_RebelAlliance_6x6");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_GalacticEmpire_6x6");
                }

            }
            else if (SizeOfBoard == BoardSize.Three_x_Three)
            {
                gridTexture = gameMain.Content.Load<Texture2D>("TicTacToe_Grid_3x3");

                if (StyleTexture == Style_Texture.Classic)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_O_3x3");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_X_3x3");
                }
                else if (StyleTexture == Style_Texture.Modern)
                {
                    oPiece = gameMain.Content.Load<Texture2D>("TicTacToe_RebelAlliance");
                    xPiece = gameMain.Content.Load<Texture2D>("TicTacToe_GalacticEmpire");
                }
            }
        }

        public void LoadOptionAndMakeNewLogic()
        {
            SizeOfBoard = (BoardSize)Enum.Parse(typeof(BoardSize), OptionSaver.Get(OptionSaver.BOARD_SIZE).ToString(), false);
            size = (int)SizeOfBoard;
            int numToWin = int.Parse(OptionSaver.Get(OptionSaver.NUM_TO_WIN).ToString());
            TicTacToePlayer theFirst = (TicTacToePlayer)Enum.Parse(typeof(TicTacToePlayer), OptionSaver.Get(OptionSaver.PLAY_FIRST).ToString(), false);
            logic = new GameLogic(numToWin, size, theFirst);

            StyleTexture = (Style_Texture)Enum.Parse(typeof(Style_Texture), OptionSaver.Get(OptionSaver.X_O_STYLE).ToString(), false);
            squareEdge = backBufferedWidth / size - 5;
        }

        public override void HandleTouch(TouchLocation touch)
        {
            if (gameScreen == Screen.Main)
            {
                HandleBoardTouch(touch);
                HandleResetTouch(touch);
                HandleMenuTouch(touch);
            }
            else if (gameScreen == Screen.Option)
            {
                gameState.HandleTouch(touch);
            }
        }

        private void HandleResetTouch(TouchLocation touch)
        {
            if (resetButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                LoadOptionAndMakeNewLogic();
            }
        }

        private void HandleMenuTouch(TouchLocation touch)
        {
            if (saveAndLoadButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                if (showingSaveButton)
                {
                    //Save button clicked                                        
                    GameSaver.Save(logic.Grid, SizeOfBoard, logic.NumToWin, logic.Current, logic.Winnable, logic.Winner);
                }
                else
                {
                    //Load button clicked
                    handleLoadClick();
                }

                showingSaveButton = !showingSaveButton;
            }

            if (optionButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                gameState = GameOption.GetInstance();
                gameState.SetGamePlay(this);
                gameScreen = Screen.Option;
            }

            if (undoButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
            }
        }

        private void handleLoadClick()
        {
            TicTacToePlayer[,] grid = GameSaver.LoadGrid();
            SizeOfBoard = (BoardSize)Enum.Parse(typeof(BoardSize), GameSaver.Load(GameSaver.KEY_BOARD_SIZE).ToString(), false);
            size = (int)SizeOfBoard;
            TicTacToePlayer theFirst = (TicTacToePlayer)Enum.Parse(typeof(TicTacToePlayer), GameSaver.Load(GameSaver.KEY_CURRENT_PLAYER).ToString(), false);
            int numToWin = int.Parse(GameSaver.Load(GameSaver.KEY_NUM_TO_WIN).ToString());
            bool winnable = bool.Parse(GameSaver.Load(GameSaver.KEY_WINNABLE).ToString());
            TicTacToePlayer winner = (TicTacToePlayer)Enum.Parse(typeof(TicTacToePlayer), GameSaver.Load(GameSaver.KEY_WINNER).ToString(), false); ;

            LoadBoardAnd_X_O();
            squareEdge = backBufferedWidth / size - 5;
            logic = new GameLogic(grid, numToWin, size, theFirst, winnable, winner);
        }

        private void HandleBoardTouch(TouchLocation touch)
        {
            if (logic.Winner == TicTacToePlayer.None)
            {
                TicTacToePlayer prev = logic.Current;
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        Rectangle box = GetGridSpace(i, j, squareEdge, squareEdge);
                        if (logic.GetPlayerAt(i, j) == TicTacToePlayer.None && box.Contains((int)touch.Position.X, (int)touch.Position.Y))
                        {
                            logic.SetPlayerAt(i, j);
                        }
                    }
                }

                logic.CheckForWin(prev);
                logic.CheckForWinnable();
            }

            if ((!logic.Winnable || logic.Winner != TicTacToePlayer.None) && !resetButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                HandleStatistics();
            }
        }

        private void HandleStatistics()
        {
            if (logic.Winner == TicTacToePlayer.PlayerO)
            {
                numOWin++;
            }
            else if (logic.Winner == TicTacToePlayer.PlayerX)
            {
                numXWin++;
            }
            else if (!logic.Winnable)
            {
                numDraw++;
            }
        }

        public override void Draw()
        {
            gameMain.GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();

            if (gameScreen == Screen.Main)
            {
                DrawMainScreen();
            }
            else if (gameScreen == Screen.Option)
            {
                DrawOptionScreen();
            }

            spriteBatch.End();
        }

        private void DrawOptionScreen()
        {
            gameState.DrawControls();
        }

        private void DrawMainScreen()
        {
            DrawGrid();
            DrawPieces();
            DrawStatus();
            DrawResetButton();
            DrawMenu();
            DrawStatistic();
        }

        private void DrawStatistic()
        {
            string textNumXWin = WrapText(statisticsFont, "X Win: " + numXWin, 160);
            // Find the center of the string
            Vector2 FontOrigin = statisticsFont.MeasureString(textNumXWin) / 2;
            int startXPos = 80;
            int yPos = 125;
            FontPos = new Vector2(startXPos, yPos);
            spriteBatch.DrawString(statisticsFont, textNumXWin, FontPos, Color.LightGreen,
                0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

            string textNumDraw = WrapText(statisticsFont, "Draw: " + numDraw, 160);
            FontOrigin = statisticsFont.MeasureString(textNumDraw) / 2;
            startXPos += 160;
            FontPos = new Vector2(startXPos, yPos);
            spriteBatch.DrawString(statisticsFont, textNumDraw, FontPos, Color.LightGreen,
                0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

            string textNumOWin = WrapText(statisticsFont, "O Win: " + numOWin, 160);
            FontOrigin = statisticsFont.MeasureString(textNumOWin) / 2;
            startXPos += 160;
            FontPos = new Vector2(startXPos, yPos);
            spriteBatch.DrawString(statisticsFont, textNumOWin, FontPos, Color.LightGreen,
                0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
        }

        private void DrawSelectBoardSize()
        {

        }

        private void DrawMenu()
        {
            if (showingSaveButton)
            {
                spriteBatch.Draw(saveButton, saveAndLoadButtonPosition, Color.White);
            }
            else
            {
                spriteBatch.Draw(loadButton, saveAndLoadButtonPosition, Color.White);
            }

            spriteBatch.Draw(optionButton, optionButtonPosition, Color.White);
            spriteBatch.Draw(undoButton, undoButtonPosition, Color.White);
        }

        private void DrawGrid()
        {
            spriteBatch.Draw(gridTexture, gridRectangle, Color.Red);
        }

        private void DrawPieces()
        {
            for (int i = 0; i < (int)SizeOfBoard; i++)
            {
                for (int j = 0; j < (int)SizeOfBoard; j++)
                {
                    if (logic.GetPlayerAt(i, j) != TicTacToePlayer.None)
                    {
                        Texture2D texture = logic.GetPlayerAt(i, j) == TicTacToePlayer.PlayerO ? oPiece : xPiece;
                        Rectangle position = GetGridSpace(i, j, texture.Width, texture.Height);
                        spriteBatch.Draw(texture, position, Color.White);
                    }
                }
            }
        }

        private void DrawStatus()
        {
            Texture2D texture;
            if (logic.Winner != TicTacToePlayer.None)
            {
                texture = logic.Winner == TicTacToePlayer.PlayerO ? oWinner : xWinner;
            }
            else if (!logic.Winnable)
            {
                texture = noWinner;
            }
            else
            {
                texture = logic.Current == TicTacToePlayer.PlayerO ? oTurn : xTurn;
            }

            Rectangle position = new Rectangle(spriteBatch.GraphicsDevice.Viewport.Width / 2 - (texture.Width / 2), 15, texture.Width, texture.Height);
            spriteBatch.Draw(texture, position, Color.White);
        }

        private void DrawResetButton()
        {
            spriteBatch.Draw(resetButton, resetButtonPosition, Color.White);
        }

        private Rectangle GetGridSpace(int column, int row, int width, int height)
        {
            int centerX = spriteBatch.GraphicsDevice.Viewport.Width / 2;
            int centerY = spriteBatch.GraphicsDevice.Viewport.Height / 2;

            int x = centerX + (column - (this.size / 2)) * squareEdge + (squareEdge - squareEdge * (this.size % 2)) / 2 - (width / 2);
            int y = centerY + (row - (this.size / 2)) * squareEdge + (squareEdge - squareEdge * (this.size % 2)) / 2 - (height / 2);

            return new Rectangle(x, y, width, height);
        }

        private string WrapText(SpriteFont spriteFont, string text, float maxLineWidth)
        {
            string[] words = text.Split(' ');

            StringBuilder sb = new StringBuilder();

            float lineWidth = 0f;

            float spaceWidth = spriteFont.MeasureString(" ").X;

            foreach (string word in words)
            {
                Vector2 size = spriteFont.MeasureString(word);

                if (lineWidth + size.X < maxLineWidth)
                {
                    sb.Append(word + " ");
                    lineWidth += size.X + spaceWidth;
                }
                else
                {
                    sb.Append("\n" + word + " ");
                    lineWidth = size.X + spaceWidth;
                }
            }

            return sb.ToString();
        }

        public override void Initialize()
        {
            LoadOptionAndMakeNewLogic();
        }
    }
}